// <file>
//     <copyright></copyright>
//     <description></description>
//     <license>
//          PROPRIETARY AND/OR CONFIDENTIAL
//     </license>
//     <owner name = "Gary Mann" email = "gmann@cc.gatech.edu"/>
//     <version>1.0</version>
// </file>

using System;
using System.Collections.Generic;
using System.IO;
using ICSharpCode.Core;
using ICSharpCode.SharpDevelop.Gui;
using ICSharpCode.SharpDevelop.Project;

namespace ICSharpCode.SharpDevelop.Commands
{
    /// <summary>
    /// Executes a custom tool associated with a file.
    /// </summary>
    public class ExecuteCustomTool : AbstractCommand
    {
        /// <summary>These are the tools that are defined by extensions.</summary>
        private static Dictionary<string, ICustomTool> tools = new Dictionary<string, ICustomTool>();

        /// <summary>
        /// Starts the command
        /// </summary>
        public override void Run()
        {
            PadDescriptor pad = WorkbenchSingleton.Workbench.GetPad(typeof (ProjectBrowserPad));

            FileNode node = ((ProjectBrowserPad) pad.PadContent).ProjectBrowserControl.SelectedNode as FileNode;

            if (node != null)
            {
                Execute(node.ProjectItem.FileName);
            }
        } // end void Run()

        /// <summary>
        /// Executes the command.
        /// </summary>
        /// <param name="fileName">The file that is being processed.</param>
        internal static void Execute(string inputFile)
        {
            FileProjectItem item = ToolGeneratedDisplayBinding.FindProjectItem(inputFile);

            if (item != null)
            {
                ICustomTool tool = GetTool(item);
                if (tool == null)
                {
                    LoggingService.InfoFormatted("The tool '{0}' was not located.", item.CustomTool);
                    return;
                }

                // Now that we have the tool, process it:
                try
                {
                    LoggingService.InfoFormatted("The tool '{0}' now generating output.", item.CustomTool);
                    IProgressMonitor progress = StatusBarService.ProgressMonitor;
                    progress.TaskName = "Generating output for " + inputFile;

                    PadDescriptor pad = WorkbenchSingleton.Workbench.GetPad(typeof (ProjectBrowserPad));
                    FileNode node = ((ProjectBrowserPad) pad.PadContent).ProjectBrowserControl.SelectedNode as FileNode;
                    string ext = "";
                    if (node.Project.Language.Equals("C#", StringComparison.InvariantCultureIgnoreCase))
                    {
                        ext = "cs";
                    }
                    else if (node.Project.Language.Equals("Boo", StringComparison.InvariantCultureIgnoreCase))
                    {
                        ext = "boo";
                    }
                    else
                    {
                        LoggingService.Warn("Unhandled file type: " + node.Project.Language);
                        return;
                    }

                    string outputFileName = Path.ChangeExtension(inputFile, ".Generated." + ext);

                    using (FileStream inputStream = File.OpenRead(inputFile))
                    {
                        using (FileStream outputStream = new FileStream(outputFileName, FileMode.Create, FileAccess.ReadWrite, FileShare.None))
                        {
                            tool.Generate(inputFile, inputStream, outputFileName, outputStream, item.CustomToolNamespace, progress);
                            LoggingService.InfoFormatted("Saved generated cs file to '{0}'", outputFileName);
                        }
                    }

                    LoggingService.InfoFormatted("The tool '{0}' done generating output.", item.CustomTool);

                    // Any output file names should be associated with the view content file
                    ReferenceOutputFileNames(inputFile, new string[] {outputFileName});
                    // Update any open views
                    IWorkbenchWindow window = FileService.GetOpenFile(inputFile);
                    if (window != null)
                    {
                        foreach (ISecondaryViewContent secondary in window.ViewContent.SecondaryViewContents)
                        {
                            ToolGeneratedSecondaryViewContent s = secondary as ToolGeneratedSecondaryViewContent;
                            if (s != null)
                            {
                                s.UpdateTextView();
                            }
                        }
                    } // end if
                }
                catch (Exception ex)
                {
                    MessageService.ShowError(ex);
                }
            } // end if
        } // end void Execute(string)

        /// <summary>
        /// Gets the tool.
        /// </summary>
        /// <param name="item">The file project item related to the resource attached
        /// to the view content.</param>
        /// <returns>Returns the tool if it could be loaded; otherwise, <c>null</c>.</returns>
        private static ICustomTool GetTool(FileProjectItem item)
        {
            // Locate the tool
            string toolName = (item.CustomTool ?? string.Empty).Trim();
            AddInTreeNode node = AddInTree.GetTreeNode("/SharpDevelop/Views/ProjectBrowser/CustomTools", false);
            ICustomTool tool = null;

            if (tools.ContainsKey(toolName))
            {
                tool = tools[toolName];
            }
            else
            {
                if (node == null)
                {
                    LoggingService.Warn("The tree node '/SharpDevelop/Views/ProjectBrowser/CustomTools' was not available.");
                }
                else
                {
                    try
                    {
                        tool = (ICustomTool) node.BuildChildItem(toolName, null, null);
                        tools.Add(toolName, tool);
                    }
                    catch
                    {
                    }
                }
            }

            return tool;
        } // end ICustomTool GetTool(FileProjectItem)

        /// <summary>
        /// Causes the output file names to be referenced by the current file.
        /// </summary>
        /// <param name="fileNames">The file being referenced</param>
        private static void ReferenceOutputFileNames(string dependentUpon, string[] fileNames)
        {
            IProject project = ProjectService.OpenSolution.FindProjectContainingFile(dependentUpon);

            foreach (string outputFileName in fileNames)
            {
                // Add the item into the project system.
                if (project != null && !project.IsFileInProject(outputFileName))
                {
                    // TODO I am not certain how to specify a default here based on the
                    // file name.  Also,  is there an API for doing this (adapted from FormDesigner
                    // code : DesignerResourceService)
                    FileProjectItem item = new FileProjectItem(project, ItemType.Compile);
                    item.DependentUpon = Path.GetFileName(dependentUpon);
                    item.Include = FileUtility.GetRelativePath(project.Directory, outputFileName);

                    ProjectService.AddProjectItem(project, item);
                    PadDescriptor pad = WorkbenchSingleton.Workbench.GetPad(typeof (ProjectBrowserPad));

                    FileNode fileNode =
                        ((ProjectBrowserPad) pad.PadContent).ProjectBrowserControl.FindFileNode(dependentUpon);
                    if (fileNode != null)
                    {
                        // LoggingService.Info("FormFileNode found, adding subitem");
                        FileNode newNode = new FileNode(outputFileName, FileNodeStatus.BehindFile);
                        newNode.AddTo(fileNode);
                        newNode.ProjectItem = item;
                    }

                    project.Save();
                }
            }
        } // end void ReferenceOutputFileNames(string, string[])
    } // end class ExecuteCustomTool
} // end namespace ICSharpCode.SharpDevelop.Commands {